﻿#include "FaceWgt.h"
#include "ui_FaceWgt.h"

#include <QDebug>

#include "FaceModule/Message.h"

FaceWgt::FaceWgt(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::FaceWgt)
{
    ui->setupUi(this);

    m_face = new FaceModule(this);
    connect(m_face, &FaceModule::sigShowMsg, this, [this](QString msg){
        ui->textEdit->append(msg);
    }, Qt::QueuedConnection);
    connect(m_face, &FaceModule::sigGetStatus, this, &FaceWgt::onGetStatus, Qt::QueuedConnection);
    connect(m_face, &FaceModule::sigVerify, this, &FaceWgt::onVerify, Qt::QueuedConnection);
    connect(m_face, &FaceModule::sigEnroll, this, &FaceWgt::onEnroll, Qt::QueuedConnection);
    connect(m_face, &FaceModule::sigDeleteAllUser, this, &FaceWgt::onDeleteAllUser, Qt::QueuedConnection);
    connect(m_face, &FaceModule::sigGetAllUserId, this, &FaceWgt::onGetAllUserId, Qt::QueuedConnection);

    connect(this, &FaceWgt::sigFindFace, this, [this](){
        if(m_autoVerify){
            if(m_verifiing) return;
            m_verifiing = true;
            s_msg_verify verify;
            verify.pd_rightaway = 0;
            verify.timeout = 10;
            m_face->onSendCommand(MID_VERIFY, &verify, 0);
        }
        else {
            if(m_enrolling) return;
            m_enrolling = true;
            s_msg_enroll_itg enroll;
            enroll.admin = ui->adminBox->currentIndex();
            QString userNameStr = ui->userNameEdit->text();
            QByteArray userName = userNameStr.toUtf8();
            memcpy(enroll.user_name, userName.data(), userName.size() < FACE_USER_NAME_SIZE ? userName.size() : FACE_USER_NAME_SIZE);
            enroll.face_direction = 0;
            enroll.face_direction |= ui->topBox->isChecked() ? 0x10 : 0x00;
            enroll.face_direction |= ui->bottomBox->isChecked() ? 0x08 : 0x00;
            enroll.face_direction |= ui->leftBox->isChecked() ? 0x04 : 0x00;
            enroll.face_direction |= ui->rightBox->isChecked() ? 0x02 : 0x00;
            enroll.face_direction |= ui->frontBox->isChecked() ? 0x01 : 0x00;
            enroll.enroll_type = ui->enrollTypeBox->currentIndex();
            enroll.enable_duplicate = ui->duplicateBox->currentIndex();
            m_face->onSendCommand(MID_ENROLL_ITG, &enroll, 0);
        }
    }, Qt::QueuedConnection);

    setupSignals();

    m_th = std::thread([this](){
        thCaptureImage();
    });
}

FaceWgt::~FaceWgt()
{
    m_capture = false;
    if(m_th.joinable()){
        m_th.join();
    }
    delete ui;
}

QPixmap FaceWgt::getCurImage()
{
    QMutexLocker locker(&m_mutex);
    return m_pix;
}

void FaceWgt::switchAutoVirtfy(bool flag)
{
    m_autoVerify = flag;
    return;
}

void FaceWgt::forceStop()
{
    m_capture = false;
    if(m_th.joinable()){
        m_th.join();
    }
    return;
}

void FaceWgt::onRemoveFace(uint16_t faceId)
{
    if(m_face){
        s_msg_delete_user temp;
        temp.user_id_heb = (faceId & 0xFF00) >> 8;
        temp.user_id_leb = (faceId & 0x00FF);
        m_face->onSendCommand(MID_RESET, &temp, sizeof(temp));
    }
    return;
}

void FaceWgt::onGetStatus(uint8_t status, QString errInfo, bool flag)
{
    if(flag){
        switch(status){
        case MS_STANDBY: ui->statusLab->setText(QString::fromLocal8Bit("设备状态：空闲")); break;
        case MS_BUSY   : ui->statusLab->setText(QString::fromLocal8Bit("设备状态：繁忙中")); break;
        case MS_ERROR  : ui->statusLab->setText(QString::fromLocal8Bit("设备状态：出错状态")); break;
        case MS_INVALID: ui->statusLab->setText(QString::fromLocal8Bit("设备状态：未初始化")); break;
        case MS_OTA    : ui->statusLab->setText(QString::fromLocal8Bit("设备状态：处于OTA状态")); break;
        }
    }
}

void FaceWgt::onVerify(uint16_t faceId, QString errInfo, bool flag)
{
    QString msg;
    m_verifiing = false;
    if(flag){
        msg = QString::fromLocal8Bit("人脸%1打卡成功").arg(faceId);
        emit sigPlayViocePrompt(msg);
        emit sigUserSign(faceId); //只在成功时发送用户签到信号.
    }
//    else {
//        msg = QString::fromLocal8Bit("人脸打卡失败，%2").arg(errInfo);
//        emit sigPlayViocePrompt(msg);
//    }

    return;
}

void FaceWgt::onEnroll(uint16_t faceId, QString errInfo, bool flag)
{
    QString msg;
    m_autoVerify = true; //主要注册结束就自动切换到鉴权解锁.
    m_enrolling = false;
    if(flag){
        msg = QString::fromLocal8Bit("人脸%1注册成功").arg(faceId);
        emit sigPlayViocePrompt(msg);
    }
//    else {
//        msg = QString::fromLocal8Bit("人脸注册失败，%2").arg(errInfo);
//        emit sigPlayViocePrompt(msg);
//    }
    emit sigUserLoginOver(flag, faceId, errInfo);
    return;
}

void FaceWgt::onDeleteAllUser(QString errInfo, bool flag)
{
    if(true == flag){
        ui->tableWidget->clearContents();
    }
    return;
}

void FaceWgt::onGetAllUserId(QByteArray faceIds, uint8_t userCnt, QString errInfo, bool flag)
{
    unsigned char *ptr = reinterpret_cast<unsigned char*>(faceIds.data());
    if(!flag){
        return;
    }

    ui->tableWidget->clearContents();
    int colCnt = ui->tableWidget->columnCount();
    if(0 == colCnt){
        ui->tableWidget->setColumnCount(10);
        colCnt = 10;
    }
    for(int i=0; i<userCnt; ++i){
        unsigned short faceId = ((ptr[2*i] << 8) | ptr[2*i+1])-1;
        int row = faceId / colCnt;
        int col = faceId % colCnt;
        QTableWidgetItem *item = ui->tableWidget->item(row, col);
        if(!item){
            item = new QTableWidgetItem;
            ui->tableWidget->setItem(row, col, item);
        }
        //item->setData(Qt::BackgroundRole, QBrush(Qt::green));
        item->setBackground(QBrush(QColor(Qt::green)));
        item->setText(QString::number(faceId + 1));
    }
    return;
}

void FaceWgt::thCaptureImage()
{
    const int fps = 10;
    const int interval = 1000/fps;
    unsigned int loopTimes = 0;
    cv::VideoCapture cap(1);
    if(!cap.isOpened()){
        qDebug() << "ERROR: Could not open camera";
        return;
    }

    //加载Haar级联人脸检测分类器.
    cv::CascadeClassifier faceDetector;
    if (!faceDetector.load("C:\\OpenCV\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_default.xml")) {
        std::cerr << "Error loading face cascade" << std::endl;
        return;
    }

    cv::Mat frame, gray, temp;
    m_capture = true;
    QImage img;
    while(m_capture){
        if(cap.read(frame) && !frame.empty()){
            cv::transpose(frame, temp); //转置.
            cv::flip(temp, frame, 1); //水平反转.
            cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY); //灰度转换，用于加速检测.

            std::vector<cv::Rect> faces; //标记人脸的位置.
            faceDetector.detectMultiScale(gray, faces, 1.1, 4, 0 | cv::CASCADE_SCALE_IMAGE, cv::Size(30, 30));

            for(const auto& face : faces) {
                cv::rectangle(gray, face, cv::Scalar(0, 255, 0), 2); //用绿色矩形框标记人脸.
                cv::rectangle(frame, face, cv::Scalar(0, 255, 0), 2); //用绿色矩形框标记人脸.
            }
            if((faces.size() >= 1) && (0 == loopTimes%(3000/interval))){
                emit sigFindFace();
            }

            img = cvMat2Image(frame);
            if(m_mutex.tryLock(10)){
                m_pix = QPixmap::fromImage(img);
                emit sigUpdateImage();
                m_mutex.unlock();
            }
            ++loopTimes;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(interval)); //10fps
    }
}

QImage FaceWgt::cvMat2Image(const cv::Mat mat)
{
    QImage img;
    switch(mat.type()){
    case CV_8UC4: //4通道ARGB格式.
        img = QImage(mat.data, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32);
        break;
    case CV_8UC3: //3通道RGB格式.
        img = QImage(mat.data, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);
        break;
    case CV_8UC1: //1通道灰度格式.
        img = QImage(mat.data, mat.cols, mat.rows, mat.step, QImage::Format_Grayscale8);
        break;
    }
    return img;
}

void FaceWgt::setupSignals()
{
    connect(ui->resetBtn, &QPushButton::clicked, this, [this]{
        m_face->onSendCommand(MID_RESET, nullptr, 0);
    }, Qt::QueuedConnection);
    connect(ui->getStatusBtn, &QPushButton::clicked, this, [this]{
        m_face->onSendCommand(MID_GET_STATUS, nullptr, 0);
    }, Qt::QueuedConnection);
    connect(ui->verifyBtn, &QPushButton::clicked, this, [this]{
        s_msg_verify verify;
        verify.pd_rightaway = 0;
        verify.timeout = 10;
        m_face->onSendCommand(MID_VERIFY, &verify, 0);
    }, Qt::QueuedConnection);
    connect(ui->enrollBtn, &QPushButton::clicked, this, [this]{
        s_msg_enroll enroll;
        enroll.admin = ui->adminBox->currentIndex();
        memset(enroll.user_name, 0, FACE_USER_NAME_SIZE);
        QString userNameStr = ui->userNameEdit->text();
        QByteArray userName = userNameStr.toUtf8();
        memcpy(enroll.user_name, userName.data(), userName.size() < FACE_USER_NAME_SIZE ? userName.size() : FACE_USER_NAME_SIZE);
        enroll.face_direction = 0;
        enroll.face_direction |= ui->topBox->isChecked() ? 0x10 : 0x00;
        enroll.face_direction |= ui->bottomBox->isChecked() ? 0x08 : 0x00;
        enroll.face_direction |= ui->leftBox->isChecked() ? 0x04 : 0x00;
        enroll.face_direction |= ui->rightBox->isChecked() ? 0x02 : 0x00;
        enroll.face_direction |= ui->frontBox->isChecked() ? 0x01 : 0x00;
        enroll.timeout = 10;
        m_face->onSendCommand(MID_ENROLL, &enroll, 0);
    }, Qt::QueuedConnection);
    connect(ui->enrollSingleBtn, &QPushButton::clicked, this, [this]{
        s_msg_enroll enroll;
        enroll.admin = ui->adminBox->currentIndex();
        QString userNameStr = ui->userNameEdit->text();
        QByteArray userName = userNameStr.toUtf8();
        memcpy(enroll.user_name, userName.data(), userName.size() < FACE_USER_NAME_SIZE ? userName.size() : FACE_USER_NAME_SIZE);
        enroll.face_direction = 0;
        enroll.face_direction |= ui->topBox->isChecked() ? 0x10 : 0x00;
        enroll.face_direction |= ui->bottomBox->isChecked() ? 0x08 : 0x00;
        enroll.face_direction |= ui->leftBox->isChecked() ? 0x04 : 0x00;
        enroll.face_direction |= ui->rightBox->isChecked() ? 0x02 : 0x00;
        enroll.face_direction |= ui->frontBox->isChecked() ? 0x01 : 0x00;
        enroll.timeout = 10;
        m_face->onSendCommand(MID_ENROLL_SINGLE, &enroll, 0);
    }, Qt::QueuedConnection);
    connect(ui->delUserBtn, &QPushButton::clicked, this, [this]{
        s_msg_delete_user user;
        user.user_id_heb = ui->userIdBox->value() / 256;
        user.user_id_leb = ui->userIdBox->value() % 256;
        m_face->onSendCommand(MID_DELETE_USER, &user, 0);
    }, Qt::QueuedConnection);
    connect(ui->delAllUserBtn, &QPushButton::clicked, this, [this]{
        m_face->onSendCommand(MID_DELETE_ALL, nullptr, 0);
    }, Qt::QueuedConnection);
    connect(ui->getAllUserIdBtn, &QPushButton::clicked, this, [this]{
        m_face->onSendCommand(MID_GET_ALL_USERID, nullptr, 0);
    }, Qt::QueuedConnection);
    connect(ui->enrollItgBtn, &QPushButton::clicked, this, [this]{
        s_msg_enroll_itg enroll;
        enroll.admin = ui->adminBox->currentIndex();
        QString userNameStr = ui->userNameEdit->text();
        QByteArray userName = userNameStr.toUtf8();
        memcpy(enroll.user_name, userName.data(), userName.size() < FACE_USER_NAME_SIZE ? userName.size() : FACE_USER_NAME_SIZE);
        enroll.face_direction = 0;
        enroll.face_direction |= ui->topBox->isChecked() ? 0x10 : 0x00;
        enroll.face_direction |= ui->bottomBox->isChecked() ? 0x08 : 0x00;
        enroll.face_direction |= ui->leftBox->isChecked() ? 0x04 : 0x00;
        enroll.face_direction |= ui->rightBox->isChecked() ? 0x02 : 0x00;
        enroll.face_direction |= ui->frontBox->isChecked() ? 0x01 : 0x00;
        enroll.enroll_type = ui->enrollTypeBox->currentIndex();
        enroll.enable_duplicate = ui->duplicateBox->currentIndex();
        m_face->onSendCommand(MID_ENROLL_ITG, &enroll, 0);
    }, Qt::QueuedConnection);

    connect(ui->clearBtn, &QPushButton::clicked, this, [this]{
        ui->textEdit->clear();
    }, Qt::QueuedConnection);
    return;
}
